package au.com.iglootech.winerymap.service.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.jdo.PersistenceManagerFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jdo.JdoTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import au.com.iglootech.winerymap.entity.IGTDomainEntity;

import com.google.appengine.api.datastore.Key;

/**
 * 
 * @author nicholas zhu
 * 
 */
@Repository(GenericDao.BEANNAME)
@Transactional(readOnly = false)
public class GenericDao {
	public static final String BEANNAME = "GenericDao";

	@Autowired
	private PersistenceManagerFactory pmf;

	private JdoTemplate jdoTemplate;

	/**
	 * create jdo template.
	 */
	@PostConstruct
	void createJdoTemplate() {
		jdoTemplate = new JdoTemplate(pmf);
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param clazz
	 *            clazz
	 * @param id
	 *            id
	 * @return entity
	 */
	@Transactional(readOnly = true)
	public <T extends IGTDomainEntity> T findByKey(final Class<T> clazz,
			final Key id) {
		T entity = jdoTemplate.detachCopy(jdoTemplate.getObjectById(clazz, id));

		if (entity == null) {
			throw new ObjectRetrievalFailureException(clazz, id);
		}
		return entity;
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param clazz
	 *            clazz
	 * @return entity
	 */
	@Transactional(readOnly = true)
	public <T extends IGTDomainEntity> Collection<T> findAll(
			final Class<T> clazz) {
		return jdoTemplate.detachCopyAll(jdoTemplate.find(clazz));
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param domainObj
	 *            entity
	 */
	@Transactional
	@SuppressWarnings("unchecked")
	public <T extends IGTDomainEntity> void remove(final T domainObj) {
		T domainObject = (T) jdoTemplate.getObjectById(domainObj.getClass(),
				domainObj.getKey());
		jdoTemplate.deletePersistent(domainObject);
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param domainObjs
	 *            entity
	 */
	@Transactional
	public <T extends IGTDomainEntity> void removeAll(
			final Collection<T> domainObjs) {
		jdoTemplate.deletePersistentAll(domainObjs);
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param clazz
	 *            clazz
	 * @param namedQuery
	 *            query
	 * @param values
	 *            parameters
	 * @return result
	 */
	@Transactional(readOnly = true)
	public <T extends IGTDomainEntity> Collection<T> findByNamedQuery(
			final Class<T> clazz, final String namedQuery,
			final Map<String, Object> values) {
		return jdoTemplate.detachCopyAll(jdoTemplate.findByNamedQuery(clazz,
				namedQuery, values));
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param clazz
	 *            clazz
	 * @param keys
	 *            keys
	 * @return result
	 */
	@Transactional(readOnly = true)
	public <T extends IGTDomainEntity> Collection<T> findByKeys(
			final Class<T> clazz, final List<Key> keys) {
		List<T> entities = new ArrayList<T>();
		for (Key key : keys) {
			entities.add(findByKey(clazz, key));
		}
		return entities;
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param domainObj
	 *            domainObj
	 * @return entity
	 */
	@Transactional
	public <T extends IGTDomainEntity> T save(final T domainObj) {
		return jdoTemplate.makePersistent(domainObj);
	}

	/**
	 * 
	 */
	@Transactional
	public void flush() {
		jdoTemplate.flush();
	}

	/**
	 * @param <T>
	 *            IGTDomainEntity
	 * @param domainObj
	 *            domainObj
	 */
	@Transactional
	public <T extends IGTDomainEntity> void refresh(final T domainObj) {
		jdoTemplate.refresh(domainObj);
	}

	/**
	 * 
	 * @return PersistenceManagerFactory
	 */
	public PersistenceManagerFactory getPmf() {
		return pmf;
	}

	/**
	 * 
	 * @return JdoTemplate
	 */
	public JdoTemplate getJdoTemplate() {
		return jdoTemplate;
	}

}
